6. 数猫をGUIアプリにする(2)
https://gyazo.com/d9bc289645c425c106bec657486fe053
Boardクラスのオブジェクトのキャンバスに手書き曲線を描いたり消したりできるようにする。
code: suunekocanvas.py
from tkinter import Tk, Frame, Button
from board import Board
import numpy as np
class SuunekoCanvas(Board):
myTk = Tk()
myTk.title(u"数猫")
base1 = Frame(myTk)
base1.pack()
base2 = Frame(myTk)
base2.pack()
Stack=[]
def __init__(self, CellSize = 60):
super(SuunekoCanvas, self).__init__(self.base1, CellSize)
self.base21 = Frame(self.base2)
self.base21.pack(side = 'bottom')
self.myCanvas.bind("<Button-2>", self.FirstPos)
self.myCanvas.bind("<B2-Motion>", self.DrawLine)
self.myCanvas.bind("<Button-3>", self.select)
self.eraseButton = Button(self.base21, text = ' erase ', command = self.erase)
self.eraseButton.pack(side="left")
self.eraseallButton = Button(self.base21, text = ' erase all ', command = self.erase_all)
self.eraseallButton.pack(side="left")
def run(self):
self.myTk.mainloop()
def FirstPos(self, event):
x, y = event.x, event.y
self.Stack.append([self.myCanvas.create_oval(x, y, x, y,
outline = 'red', fill = 'red')])
def DrawLine(self, event):
x, y = event.x, event.y
pos = self.myCanvas.coords(p)
self.Stack-1.append(self.myCanvas.create_line(u, v, x, y, width = 4, fill = 'red'))
def erase(self):
if self.Stack != []:
self.myCanvas.delete(p)
self.Stack.pop(-1)
def erase_all(self):
while self.Stack != []:
self.erase()
def get_seg(self, x, y):
for seg in self.Stack:
for p in seg:
pos = self.myCanvas.coords(p)
return seg
def seg2ijn(self, seg):
S = [self.myCanvas.coords(s):2 for s in seg] N = len(S)
m = np.sum(np.array(S), axis = 0) / N
i, j, n = self.xy2ijn(self.CellSize // 3, int(m0), int(m1)) return i, j, n, m, V
def del_seg(self, seg):
for p in seg:
self.myCanvas.delete(p)
self.Stack.remove(seg)
def is_blank(self, i, j):
return self.get_number(i, j) == 0
def select(self, event):
seg = self.get_seg(event.x, event.y)
if seg:
i, j, n, m, V = self.seg2ijn(seg)
if V < self.CellSize / 4:
if self.is_blank(i, j):
pos = np.array(self.ijn2xy(self.CellSize // 3, i, j, n))
if np.linalg.norm(m - pos) < self.CellSize / 15:
self.change_color(i, j, n, color = 'red')
elif V < 2 * self.CellSize // 3:
if n == 5:
if self.is_blank(i, j):
for n in self.seek(i, j):
self.mark(i, j, n)
else:
p = self.get_number(i, j)
for k, l in self.row(i):
self.mark(k, l, p)
for k, l in self.column(j):
self.mark(k, l, p)
for k, l in self.block(i // 3, j // 3):
self.mark(k, l, p)
self.del_seg(seg)
def fix(self, i, j, n):
for k in range(1, 10):
self.hide(i, j, k)
self.change_number(i, j, n)
self.change_color(i, j, 0, color = 'black')
def seek(self, i, j):
def mark(self, i, j, n):
if self.is_blank(i, j):
if n != 0 and self.get_color(i, j, n) == 'grey50':
self.change_color(i, j, n, 'red')
if __name__ == "__main__":
suuneko = SuunekoCanvas()
for i in range(9):
for j in range(9):
if n:
suuneko.fix(i, j, n)
suuneko.run()
クラス変数:
myTktKのベースとなるフレーム
base1キャンバスを配置するフレーム
base2ボタンを配置するフレーム
Stack手書き曲線のオブジェクトを記憶するためのスタック
base21base2フレームのサブフレーム、手書き曲線を消去するボタンを配置
row各行の添字のリスト
column各列の添字のリスト
block各$ 3\times 3のブロックの添字の2次元リスト
メッソド:
runメインループを起動する
FirstPosマウスの右ボタンを押してキャンバス上の最初の点の座標を取得して、小さな点を打つ
DrawLineマウスの右ボタンをドラッグしながら曲線を書く。ボタンを離すと、曲線の情報がスタックに記憶される。
erase最後に書かれた手書きの曲線を消す
eraze_all手書きの曲線を全て消す
get_segスタックにある曲線のオブジェクトのうち、与えられた$ xy座標に近いもの
seg2ijn曲線を構成する点の平均値mと標準偏差V、およびそれらから割り出したマス目の添字i, jとそのセル内の小数字n
del_seg曲線を消去
is_blanki行j列のマス目が空欄であるとき真を返す
selectマウスの中央クリックで曲線を選択して消す。このとき、曲線が小さな数字の特定できるほどの位置と大きさならば、その小さな数字のステータスを赤の変える。
fixi行j列のマス目の小さな数字をすべて隠して、大きな数字をnに変えて表示する
marki行j列のマス目に表示されている小さな数字のnを赤字にする
マウスの第2ボタンのドラッグで曲線を描く
マウスの第3ボタンのクリックで曲線を消す、このとき曲線がマス目の小数字を特定できるほどの大きさであるならば、その小数字を赤字にする
eraseボタンは最後に書かれた曲線を消去する
erase allはスタックにあるすべての曲線を消去する